package com.own.business.resource.core;

import com.alibaba.fastjson2.JSON;
import com.own.business.resource.core.base.AbstractObjectStoreUtil;
import com.own.business.resource.core.property.ResourceProperty;
import com.own.business.resource.core.property.ResourceSourceProperty;
import com.own.business.resource.core.util.LocalObjectStoreUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * ResolveObjectStoreUtil
 *
 * @author chenxueli
 * @date 2023-07-07 14:31:23
 */
@Slf4j
@Component
public class ResolveObjectStoreUtil {

    /**
     * 所有的工具的键值对象
     */
    private static final Map<String, AbstractObjectStoreUtil> BASE_OBJECT_STORE_UTIL_MAP = new HashMap<>(8);
    /**
     * 位置配置信息
     */
    private static final Map<String, ResourceSourceProperty> BASE_OBJECT_STORE_UTIL_PROPERTY_MAP = new HashMap<>(8);
    /**
     * 位置和方式的映射
     */
    private static final Map<String, String> BASE_POSITION_METHOD_MAP = new HashMap<>(8);
    /**
     * 默认的方式的名称
     */
    private static final String DEFAULT_METHOD_NAME = "default";
    /**
     * 本机的地址
     */
    private static final String LOCALHOST = "localhost";
    /**
     * 默认工具
     */
    private static AbstractObjectStoreUtil DEFAULT_UTIL = null;
    @Resource
    private ResourceProperty resourceProperty;

    /**
     * 获取上传的方式
     *
     * @return 上传方式对象
     */
    public static AbstractObjectStoreUtil get() {
        return DEFAULT_UTIL;
    }

    /**
     * 获取上传的方式
     *
     * @param name 名称/key
     * @return 上传方式对象
     */
    public static AbstractObjectStoreUtil get(String name) {
        return BASE_OBJECT_STORE_UTIL_MAP.getOrDefault(name, DEFAULT_UTIL);
    }

    /**
     * 获取默认的上传文件的方式
     *
     * @return 默认的上传文件的方式
     */
    public static String defaultMethodName() {
        return DEFAULT_UTIL != null ? DEFAULT_UTIL.getName() : DEFAULT_METHOD_NAME;
    }

    /**
     * 根据位置获取资源属性
     *
     * @param position 位置
     * @return 资源属性
     */
    public static ResourceSourceProperty getSourcePropertyByPosition(String position) {
        return BASE_OBJECT_STORE_UTIL_PROPERTY_MAP.get(position);
    }

    /**
     * 根据位置获取上传方式
     *
     * @param position 位置
     * @return 资源属性
     */
    public static String getMethodByPosition(String position) {
        return BASE_POSITION_METHOD_MAP.get(position);
    }

    @PostConstruct
    public void init() {
        // 加载单个方式源
        var source = resourceProperty.getSource();
        if (source != null) {
            var util = resolveForClassName(DEFAULT_METHOD_NAME, source);
            if (util == null) {
                log.warn("source配置的上传实现未扫描到");
            } else {
                // 设置默认的信息
                BASE_OBJECT_STORE_UTIL_MAP.put(DEFAULT_METHOD_NAME, util);
                DEFAULT_UTIL = util;
                if (StringUtils.isNotBlank(source.getPosition())) {
                    BASE_OBJECT_STORE_UTIL_PROPERTY_MAP.put(source.getPosition(), source);
                }
            }
        }
        // 加载多个方式源
        var multipleSource = resourceProperty.getMultipleSource();
        if (multipleSource != null && !multipleSource.isEmpty()) {
            // 解析数据
            multipleSource.forEach((name, config) -> {
                var object = resolveForClassName(name, config);
                if (object == null) {
                    log.warn("multiple {}source配置的上传实现未扫描到", name);
                } else {
                    BASE_OBJECT_STORE_UTIL_MAP.put(name, object);
                    if (StringUtils.isNotBlank(config.getPosition())) {
                        BASE_OBJECT_STORE_UTIL_PROPERTY_MAP.put(config.getPosition(), config);
                    }
                }
            });
        }
        // 如果一个方式都没有的话需要添加一个default，默认为本地上传
        if (BASE_OBJECT_STORE_UTIL_MAP.isEmpty()) {
            log.warn("没有扫描到任何可用的上传实现，默认方法重置为本地上传");
            var util = new LocalObjectStoreUtil();
            DEFAULT_UTIL = util;
            BASE_OBJECT_STORE_UTIL_MAP.put(DEFAULT_METHOD_NAME, util);
        }
        // 添加自定义的默认方式
        var primary = resourceProperty.getPrimary();
        // 如果为空
        if (StringUtils.isNotEmpty(primary)) {
            DEFAULT_UTIL = BASE_OBJECT_STORE_UTIL_MAP.get(resourceProperty.getPrimary());
            if (DEFAULT_UTIL == null) {
                log.error("当前上传资源默认位置 {} 不存在", resourceProperty.getPrimary());
            }
        }
        log.info("register file upload type {}个：{}", BASE_OBJECT_STORE_UTIL_MAP.size(), JSON.toJSONString(BASE_OBJECT_STORE_UTIL_MAP.keySet()));
        log.info("default file upload type：{}", DEFAULT_UTIL.getName());
        BASE_OBJECT_STORE_UTIL_MAP.forEach((key, value) -> {
            BASE_POSITION_METHOD_MAP.put(value.getProperties().getPosition(), key);
        });
    }

    /**
     * 数据解析（className）
     *
     * @param name     方式名称
     * @param property 配置信息
     * @return 解析后的对象信息
     */
    private AbstractObjectStoreUtil resolveForClassName(String name, ResourceSourceProperty property) {
        if (StringUtils.isEmpty(property.getClassName())) {
            return resolveForEnum(name, property);
        }
        Class<?> clazz;
        try {
            clazz = Class.forName(property.getClassName());
        } catch (ClassNotFoundException e) {
            return resolveForEnum(name, property);
        }
        return resolve(name, clazz, property);
    }

    /**
     * 数据解析（enum）
     *
     * @param name     方式名称
     * @param property 配置信息
     * @return 解析后的对象信息
     */
    private AbstractObjectStoreUtil resolveForEnum(String name, ResourceSourceProperty property) {
        if (property.getType() == null) {
            return null;
        }
        Class<?> clazz;
        try {
            clazz = Class.forName(property.getType().className());
        } catch (ClassNotFoundException e) {
            return null;
        }
        return resolve(name, clazz, property);
    }

    /**
     * 解析数据
     *
     * @param name     方式名称
     * @param clazz    类
     * @param property 配置信息
     * @return 解析后的对象信息
     */
    private AbstractObjectStoreUtil resolve(String name, Class<?> clazz, ResourceSourceProperty property) {
        var parent = clazz.getSuperclass();
        if (parent != AbstractObjectStoreUtil.class) {
            log.error("资源管理：{}的方法没有继承{}", name, AbstractObjectStoreUtil.class.getCanonicalName());
            return null;
        }
        AbstractObjectStoreUtil object;
        try {
            object = (AbstractObjectStoreUtil) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
        if (LOCALHOST.equals(property.getPosition())) {
            property.setPosition("");
        }
        // 设置验证并返回信息
        return object.setConfigAndCheck(name, property) ? object : null;
    }

}
